package com.tempjob.app.service;

import com.alibaba.fastjson.JSON;
import com.tempjob.app.vo.wechat.WexinTokenResp;
import com.tempjob.common.businesss.mongo.entity.CompanyInfo;
import com.tempjob.common.businesss.mongo.entity.UserInfo;
import com.tempjob.common.businesss.service.MongoBeanUtil;
import com.tempjob.common.context.UserContext;
import com.tempjob.common.exception.BusinessException;
import com.tempjob.common.model.ResponseCode;
import com.tempjob.common.utils.CachedBeanCopier;
import com.tempjob.common.utils.HttpUtils;
import com.tempjob.common.utils.PasswordEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author hui
 * @Date 2023/12/15 22:56
 **/

@Slf4j
@Service
public class TokenService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Value("${server.isdev:false}")
    private boolean isdev;

    @Value("${wechat.app.id:wx1e5520ea9486f361}")
    private String wechatAppId;

    @Value("${wechat.app.key:905045c7314f1624af5635c909170d62}")
    private String wechatAppSecret;
    private static String WEXIN_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    public static final String TICKET_PREFIX = "xishu:tempjob:user:ticket:";

    public static final String LOGIN_TICKET_PREFIX = "xishu:tempjob:user:ticketList:";

    @Value("${spring.application.expire:86400}")
    private long tokenExpire;

    @Autowired
    private MongoBeanUtil mongoBeanUtil;


    /**
     * 获取微信accessToken
     *
     * @param reflash
     * @return
     */
    public String getWechatAccessToken(Boolean reflash) {
        String token = null;
        String tokenKey = "wechat_access_token";
        String tokenLockKey = "get_wechat_access_token_lock";
        if (!reflash) {
            token = redisTemplate.opsForValue().get(tokenKey);
        }
        if (token == null) {
            if (!redisTemplate.opsForValue().setIfAbsent(tokenLockKey, "1", 1, TimeUnit.MINUTES)) {
                throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "系统繁忙，请稍后重试");
            }
            try {
                String tokenUrl = String.format(WEXIN_TOKEN_URL, wechatAppId, wechatAppSecret);
                String response = HttpUtils.sendGet(tokenUrl, null,  null);
                log.info("getWechatAccessTokenFromWechatResult" + response);
                if (!StringUtils.isEmpty(response)) {
                    WexinTokenResp resp = JSON.parseObject(response, WexinTokenResp.class);
                    if (null != resp.getAccess_token()) {
                        token = resp.getAccess_token();
                        long expirTime = (resp.getExpires_in() - 100);
                        redisTemplate.opsForValue().set(tokenKey, token, expirTime, TimeUnit.SECONDS);
                    }
                }
                if (StringUtils.isEmpty(token)) {
                    log.error("getWechatAccessTokenResult:{},{}", reflash, response);
                }
            } catch (Exception e) {
                log.error("getWechatAccessTokenError,{}", reflash, e);
            } finally {
                redisTemplate.delete(tokenLockKey);
            }
        }
        return token;
    }


    public String createTicket(UserContext userInfoContext, String ticket) {
        if (StringUtils.isBlank(ticket)) {
            ticket = UUID.randomUUID().toString().replace("-", "") + PasswordEncoder.hexMD5(userInfoContext.getId() + userInfoContext.getRealName() + userInfoContext.getMobile() + new Date().getTime());
            log.info("initTicket_getTicketKey:{}，{}", userInfoContext, ticket);
        }
        String ssoKey = TICKET_PREFIX + ticket;
        String ssoIdKey = LOGIN_TICKET_PREFIX + userInfoContext.getId();
        String ticketsString = redisTemplate.opsForValue().get(ssoIdKey);
        List<String> tickets = new ArrayList<>();
        if (ticketsString != null && !StringUtils.isEmpty(ticketsString)) {
            tickets = JSON.parseArray(ticketsString, String.class);
            for (String s : tickets) {
                redisTemplate.delete(TICKET_PREFIX + s);
            }
            tickets = new ArrayList<>();
        }
        tickets.add(ticket);
        redisTemplate.opsForValue().set(ssoKey, JSON.toJSONString(userInfoContext), tokenExpire, TimeUnit.SECONDS);
        redisTemplate.opsForValue().set(ssoIdKey, JSON.toJSONString(tickets), tokenExpire, TimeUnit.SECONDS);
//        redisTemplate.opsForValue().set(ssoIdKey, JSON.toJSONString(tickets));
        return ticket;
    }

    public String createTicket(String userId, String ticket) {
        UserContext userInfoContext = this.getUserContext(userId);
        return this.createTicket(userInfoContext, ticket);
    }


    public void logout(String ticket) {
        //删除用户对应ticket
        String ssoKey = LOGIN_TICKET_PREFIX + ticket;
//        redisTemplate.delete(ssoKey);
        String user = redisTemplate.opsForValue().get(ssoKey);
        if (!StringUtils.isEmpty(user)) {
            UserContext userInfo = JSON.parseObject(user, UserContext.class);
            if (userInfo != null) {
                String ssoIdKey = TICKET_PREFIX + userInfo.getId();
                String ticketsString = redisTemplate.opsForValue().get(ssoIdKey);
                if (ticketsString != null && !StringUtils.isEmpty(ticketsString)) {
                    List<String> tickets = JSON.parseArray(ticketsString, String.class);
                    tickets.forEach(t -> redisTemplate.delete(TICKET_PREFIX + t));
                }
                redisTemplate.delete(ssoIdKey);
            }
            redisTemplate.delete(ssoKey);
        }
    }

    public UserContext validateTicket(String ticket) {
        // 获取用户信息token
        String ssoKey = TICKET_PREFIX + ticket;
        String user = redisTemplate.opsForValue().get(ssoKey);
        if (!StringUtils.isEmpty(user)) {
            UserContext userInfo = JSON.parseObject(user, UserContext.class);
            if (userInfo != null) {
                //刷新过期时间
                String ssoIdKey = LOGIN_TICKET_PREFIX + userInfo.getId();
                redisTemplate.expire(ssoKey, tokenExpire, TimeUnit.SECONDS);
                redisTemplate.expire(ssoIdKey, tokenExpire, TimeUnit.SECONDS);
                return userInfo;
            }
        }
        return null;
//        throw new BusinessException(ResponseCode.BUSINESS_EXCEPTION, "用户ticket无效！");
    }


    public String reflashTicket(UserContext userInfoContext, String ticket) {
        String ssoKey = TICKET_PREFIX + ticket;
        redisTemplate.opsForValue().set(ssoKey, JSON.toJSONString(userInfoContext), tokenExpire, TimeUnit.SECONDS);
        return ticket;
    }


    public UserContext getUserContext(String userId) {
        UserInfo user = mongoBeanUtil.getUserInfoService().getById(userId);
        if (user.getUserStatus().equals(-1)) {
            throw new BusinessException("已禁用，请联系管理员");
        }
        UserContext context = new UserContext();
        CachedBeanCopier.copyObject(user, context);
        if (!user.getUserType().contains(2)) {
            if (StringUtils.isNoneBlank(user.getCompanyId())) {
                CompanyInfo companyInfo = mongoBeanUtil.getCompanyInfoService().getById(user.getCompanyId());
                context.setCompanyInfo(companyInfo);
            }
        }
        return context;
    }

    public void abortUserByUserIds(List<Long> userIds) {
        userIds.forEach(i -> {
            String ssoIdKey = LOGIN_TICKET_PREFIX + i;
            String ssoKey = redisTemplate.opsForValue().get(ssoIdKey);
            if (!StringUtils.isEmpty(ssoKey)) {
                List<String> tickets = JSON.parseArray(ssoKey, String.class);
                tickets.forEach(t -> redisTemplate.delete(TICKET_PREFIX + t));
                redisTemplate.delete(ssoKey);
            }
        });
    }

    public void abortUserByUserId(Long userId) {
        String ssoIdKey = LOGIN_TICKET_PREFIX + userId;
        String ssoKey = redisTemplate.opsForValue().get(ssoIdKey);
        if (!StringUtils.isEmpty(ssoKey)) {
            List<String> tickets = JSON.parseArray(ssoKey, String.class);
            tickets.forEach(t -> redisTemplate.delete(TICKET_PREFIX + t));
            redisTemplate.delete(ssoKey);
        }
    }

}
